using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;


namespace LightCAD.Three
{
    public class TorusGeometry : BufferGeometry
    {
        public class Parameters
        {
            public double radius;
            public double tube;
            public int radialSegments;
            public int tubularSegments;
            public double arc;
        }
        #region Properties

        public Parameters parameters;

        #endregion

        #region constructor
        public TorusGeometry(double radius = 1, double tube = 0.4, int radialSegments = 12, int tubularSegments = 48, double arc = Math.PI * 2)
        {
            this.type = "TorusGeometry";
            this.parameters = new Parameters()
            {
                radius = radius,
                tube = tube,
                radialSegments = radialSegments,
                tubularSegments = tubularSegments,
                arc = arc
            };
            //radialSegments = (int)Math.Floor(radialSegments);
            //tubularSegments = (int)Math.Floor(tubularSegments);
            // buffers
            var indices = new ListEx<int>();
            var vertices = new ListEx<double>();
            var normals = new ListEx<double>();
            var uvs = new ListEx<double>();
            // helper variables
            var center = new Vector3();
            var vertex = new Vector3();
            var normal = new Vector3();
            // generate vertices, normals and uvs
            for (int j = 0; j <= radialSegments; j++)
            {
                for (int i = 0; i <= tubularSegments; i++)
                {
                    var u = (double)i / tubularSegments * arc;
                    var v = (double)j / radialSegments * MathEx.PI * 2;
                    // vertex
                    vertex.X = (radius + tube * Math.Cos(v)) * Math.Cos(u);
                    vertex.Y = (radius + tube * Math.Cos(v)) * Math.Sin(u);
                    vertex.Z = tube * Math.Sin(v);
                    vertices.Push(vertex.X, vertex.Y, vertex.Z);
                    // normal
                    center.X = radius * Math.Cos(u);
                    center.Y = radius * Math.Sin(u);
                    normal.SubVectors(vertex, center).Normalize();
                    normals.Push(normal.X, normal.Y, normal.Z);
                    // uv
                    uvs.Push((double)i / tubularSegments);
                    uvs.Push((double)j / radialSegments);
                }
            }
            // generate indices
            for (int j = 1; j <= radialSegments; j++)
            {
                for (int i = 1; i <= tubularSegments; i++)
                {
                    // indices
                    var a = (tubularSegments + 1) * j + i - 1;
                    var b = (tubularSegments + 1) * (j - 1) + i - 1;
                    var c = (tubularSegments + 1) * (j - 1) + i;
                    var d = (tubularSegments + 1) * j + i;
                    // faces
                    indices.Push(a, b, d);
                    indices.Push(b, c, d);
                }
            }
            // build geometry
            this.setIndex(indices.ToArray());
            this.setAttribute("position", new Float32BufferAttribute(vertices.ToArray(), 3));
            this.setAttribute("normal", new Float32BufferAttribute(normals.ToArray(), 3));
            this.setAttribute("uv", new Float32BufferAttribute(uvs.ToArray(), 2));
        }
        #endregion

        #region methods
        public TorusGeometry copy(TorusGeometry source)
        {
            base.copy(source);
            this.parameters = new Parameters()
            {
                radius = source.parameters.radius,
                tube = source.parameters.tube,
                radialSegments = source.parameters.radialSegments,
                tubularSegments = source.parameters.tubularSegments,
                arc = source.parameters.arc,
            };
            return this;
        }
        #endregion
    }
}
